package com.emergency.embackend.service.impl;

import com.emergency.embackend.common.PageParams;
import com.emergency.embackend.entity.Article;
import com.emergency.embackend.entity.Video;
import com.emergency.embackend.mapper.OperateMapper;
import com.emergency.embackend.param.ArticleDataParam;
import com.emergency.embackend.exception.EmException;
import com.emergency.embackend.mapper.WikiArticleMapper;
import com.emergency.embackend.param.ArticleParam;
import com.emergency.embackend.service.DataViewService;
import com.emergency.embackend.service.OperateService;
import com.emergency.embackend.service.WikiArticleService;
import com.emergency.embackend.utils.RecommendUtils;
import com.emergency.embackend.utils.RedisConstants;
import com.emergency.embackend.utils.UserHolder;
import com.emergency.embackend.vo.ArticleVO;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;


@Service
public class WikiArticleServiceImpl implements WikiArticleService {
    @Autowired
    private WikiArticleMapper wikiArticleMapper;
    @Autowired
    private OperateService operateService;
    @Autowired
    private DataViewService dataViewService;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private OperateMapper operateMapper;

    @Override
    public PageInfo getArticleData(ArticleDataParam articleDataParam) {
        PageHelper.startPage(articleDataParam.getPageNum(), articleDataParam.getPageSize());
        List<ArticleVO> resList = wikiArticleMapper.getArticleData(articleDataParam);
        resList.forEach(article -> {
            article.setViewCount(dataViewService.getViewCount(RedisConstants.VIEW_ARTICLE_COUNT_KEY + article.getId()) + article.getViewCount());
            article.setLikeCount(operateService.getLikeOrCollectCount(RedisConstants.ARTICLE_LIKE_COUNT + article.getId()));
            article.setCollectCount(operateService.getLikeOrCollectCount(RedisConstants.ARTICLE_COLLECT_COUNT + article.getId()));
        });
        PageInfo pageInfo = new PageInfo(resList);
        return pageInfo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(Long articleId) {
        wikiArticleMapper.delete(articleId);
        stringRedisTemplate.delete(RedisConstants.ARTICLE_COLLECT_COUNT+articleId);
        stringRedisTemplate.delete(RedisConstants.ARTICLE_LIKE_COUNT+articleId);
        stringRedisTemplate.delete(RedisConstants.VIEW_ARTICLE_COUNT_KEY+articleId);
        stringRedisTemplate.delete(RedisConstants.VIEW_ARTICLE_REPEAT_KEY+articleId);
    }

    @Override
    public void deleteByTypeId(Long typeId) {
        wikiArticleMapper.deleteByTypeId(typeId);
    }

    @Override
    public ArticleVO getOne(Long articleId) {
        ArticleVO article = wikiArticleMapper.getOne(articleId);
        if (article == null) {
            throw new EmException("500", "查询结果为空");
        }
        //流量的添加
        if (UserHolder.getUser() != null) {
            Long userId = UserHolder.getUser().getId();
            operateService.addViews(articleId, userId, '1');
            article.setIsLike(isLike(article.getId(), userId));
            article.setIsCollect(isCollect(article.getId(), userId));
        } else {
            operateService.addViews(articleId, null, '1');
        }
        //获取redis中的浏览量 并且与现有的浏览量相加
        article.setViewCount(dataViewService.getViewCount(RedisConstants.VIEW_ARTICLE_COUNT_KEY + articleId) + article.getViewCount());
        article.setLikeCount(operateService.getLikeOrCollectCount(RedisConstants.ARTICLE_LIKE_COUNT + articleId));
        article.setCollectCount(operateService.getLikeOrCollectCount(RedisConstants.ARTICLE_COLLECT_COUNT + article.getId()));
        return article;
    }

    @Override
    public void update(ArticleParam articleParam) {
        articleParam.setUpdateTime(new Date());
        wikiArticleMapper.update(articleParam);
    }

    @Override
    public void add(ArticleParam articleParam) {
        Date date = new Date();
        articleParam.setCreateTime(date);
        articleParam.setUpdateTime(date);
        wikiArticleMapper.add(articleParam);
    }

    @Override
    public PageInfo getArticlesByCreateTimeDesc(PageParams pageParams) {
        PageHelper.startPage(pageParams.getPageNum(), pageParams.getPageSize());
        List<ArticleVO> res = wikiArticleMapper.selectArticlesByCreateTimeDesc();
        res.forEach(article -> {
            article.setViewCount(dataViewService.getViewCount(RedisConstants.VIEW_ARTICLE_COUNT_KEY + article.getId()) + article.getViewCount());
            article.setLikeCount(operateService.getLikeOrCollectCount(RedisConstants.ARTICLE_LIKE_COUNT + article.getId()));
            article.setCollectCount(operateService.getLikeOrCollectCount(RedisConstants.ARTICLE_COLLECT_COUNT + article.getId()));
        });
        PageInfo pageInfo = new PageInfo(res);
        return pageInfo;
    }

    @Override
    public List<ArticleVO> getHotArticle() {
        List<ArticleVO> hotArticle = wikiArticleMapper.getHotArticle();
        if (hotArticle == null) {
            throw new EmException("500", "未获取到最热数据..");
        }
        hotArticle.forEach(article -> {
            article.setViewCount(dataViewService.getViewCount(RedisConstants.VIEW_ARTICLE_COUNT_KEY + article.getId()) + article.getViewCount());
            article.setLikeCount(operateService.getLikeOrCollectCount(RedisConstants.ARTICLE_LIKE_COUNT + article.getId()));
            article.setCollectCount(operateService.getLikeOrCollectCount(RedisConstants.ARTICLE_COLLECT_COUNT + article.getId()));
        });
        return hotArticle;
    }

    @Override
    public List<ArticleVO> getRecommendArticle(ArticleDataParam articleDataParam) {
        List<ArticleVO> articleData = wikiArticleMapper.getArticleData(articleDataParam);
        Random random = new Random();
        //从redis中获取对应的点赞数和收藏数
        articleData.forEach(e -> {
            e.setViewCount(dataViewService.getViewCount(RedisConstants.VIEW_VIDEO_COUNT_KEY + e.getId()) + e.getViewCount());
            e.setLikeCount(operateService.getLikeOrCollectCount(RedisConstants.VIDEO_LIKE_COUNT + e.getId()));
            e.setCollectCount(operateService.getLikeOrCollectCount(RedisConstants.VIDEO_COLLECT_COUNT + e.getId()));
        });
        List<ArticleVO> resList = new ArrayList<>();
        Long userId = articleDataParam.getUserId();
        Collections.shuffle(articleData);
        if (userId == null) {
            if (articleDataParam.getLimitNum() != null) {
                return articleData.subList(0, articleDataParam.getLimitNum().intValue());
            }
            return articleData;
        }
        String key = RedisConstants.VIEW_ARTICLE_HISTORY_KEY + userId;
        //从redis获取用户浏览过的文章的历史
        Set<String> history = stringRedisTemplate.opsForZSet().range(key, 0, -1);
        Map<Long, Integer> map = new HashMap<>();
        if (history != null && history.size() != 0) {
            List<Long> articleIds = history.stream().map(e -> Long.parseLong(e)).collect(Collectors.toList());
            buildDataType(articleIds, map);
        }
        //在从数据库获取用户点赞和收藏的对应的文章数据
        List<Long> likeIds = operateMapper.getLikeData("1", userId);
        if (likeIds != null && likeIds.size() != 0) {
            buildDataType(likeIds, map);
        }
        List<Long> collectIds = operateMapper.getCollectData("1", userId);
        if (collectIds != null && collectIds.size() != 0) {
            buildDataType(collectIds, map);
        }
        //通过map集合存储每种类型 该用户浏览的类型比例
        if (map.isEmpty()) {
            if (articleDataParam.getLimitNum() != null) {
                return articleData.subList(0, articleDataParam.getLimitNum().intValue());
            }
            return articleData;
        }
        List<Long> typeIds = map.entrySet().stream().sorted(Collections
                        .reverseOrder(Map.Entry.comparingByValue()))
                .map(Map.Entry::getKey).collect(Collectors.toList());
        //根据该类型比例去获取对应的文章数据
        int limitNum = -1;
        if (articleDataParam.getLimitNum() != null) {
            limitNum = articleDataParam.getLimitNum().intValue();
        }
        for (Long typeId : typeIds) {
            ArticleDataParam param = new ArticleDataParam();
            param.setTypeId(typeId);
            List<ArticleVO> recommendData = wikiArticleMapper.getArticleData(param);
            Collections.shuffle(recommendData);
            if (limitNum > 0) {
                if (recommendData.size() >= limitNum) {
                    resList.addAll(recommendData.subList(0, limitNum));
                    break;
                } else {
                    limitNum -= recommendData.size();
                    resList.addAll(recommendData);
                }
            } else {
                resList = wikiArticleMapper.getArticleData(param);
            }
        }
        return resList;
    }

    @Override
    public List<ArticleVO> getHistory(Long userId) {
        //获取对应的文章id值
        List<ArticleVO> resList = null;
        String key = RedisConstants.VIEW_ARTICLE_HISTORY_KEY + userId;
        if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(key))) {
            List<Long> articleIds = new ArrayList<>();
            Set<String> set = stringRedisTemplate.opsForZSet().range(key, 0, -1);
            if (set != null && set.size() != 0) {
                articleIds = set.stream().map(Long::parseLong).collect(Collectors.toList());
                resList = wikiArticleMapper.getHistoryArticle(articleIds);
            }
        }
        return resList;
    }

    private void buildDataType(List<Long> articleIds, Map<Long, Integer> map) {
        List<Long> typeIds = wikiArticleMapper.getTypeIds(articleIds);
        typeIds.forEach(e -> {
            map.put(e, map.getOrDefault(e, 0) + 1);
        });
    }

    private Boolean isLike(Long articleId, Long userId) {
        String key = RedisConstants.ARTICLE_LIKE_USER + articleId;
        Boolean flag = false;
        if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(key))) {
            flag = stringRedisTemplate.opsForSet().isMember(key, userId.toString());
        }
        return flag;
    }

    private Boolean isCollect(Long articleId, Long userId) {
        String key = RedisConstants.ARTICLE_COLLECT_USER + articleId;
        Boolean flag = false;
        if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(key))) {
            flag = stringRedisTemplate.opsForSet().isMember(key, userId.toString());
        }
        return flag;
    }
}
